#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "hal/hal_io.h"
#include "common/config.h"
#include "hal_mcu.h"
#include "userLog.h"

#define HAL_IO_CFG_FILE T_CONFIG_ROOT_PATH "/hal_io.ini"
#define HAL_IO_GET_VALUE_PATH(io, buf)                    \
	do                                                    \
	{                                                     \
		sprintf(buf, "/sys/class/gpio/gpio%d/value", io); \
	} while (0) // 输入输出电平值设备
#define HAL_IO_GET_DIRECT_PATH(io, buf)                       \
	do                                                        \
	{                                                         \
		sprintf(buf, "/sys/class/gpio/gpio%d/direction", io); \
	} while (0)										// GPIO输入输出控制设备
#define HAL_IO_EXPORT_PATH "/sys/class/gpio/export" // GPIO设备导出设备
#define HAL_IO_DIR_OUT "out"
#define HAL_IO_DIR_IN "in"
#define HAL_IO_HIGH_LEVEL "1"
#define HAL_IO_LOW_LEVEL "0"
#define HAL_IO_NAME_LEN (32)

typedef struct _HAL_IO_CONTEXT_S
{
	T_INT32 nLoopRun;
	T_CALLBACK afnLoopCallBack[HAL_IO_MAX_LOOP];
} HAL_IO_CONTEXT_S;

typedef struct _HAL_IO_HUMAN_CFG_S
{
	char acName[HAL_IO_NAME_LEN];
	T_UINT32 *pu32IoNum;
} HAL_IO_HUMAN_CFG_S;

typedef struct _HAL_IO_MAP_S
{
	char *pcName;
	T_UINT32 uIoNum;
} HAL_IO_MAP_S;

static HAL_IO_CONFIG_S gsstHalIoCfgs;
static HAL_IO_CONTEXT_S *pstHalIoCtx = NULL;

#if 0
static CONFIG_RECORD_S astRecords[] =
{
	{"u32RailUpIO", &gsstHalIoCfgs.u32RailUpIO, CONFIG_TYPE_STRING},
	{"au32LoopIO0", &gsstHalIoCfgs.au32LoopIO[0], CONFIG_TYPE_STRING},
	{"au32LoopIO1", &gsstHalIoCfgs.au32LoopIO[1], CONFIG_TYPE_STRING},
	{"au32LoopIO2", &gsstHalIoCfgs.au32LoopIO[2], CONFIG_TYPE_STRING},
	{"au32LoopIO3", &gsstHalIoCfgs.au32LoopIO[3], CONFIG_TYPE_STRING},
	{"u32CapLight", &gsstHalIoCfgs.u32CapLight, CONFIG_TYPE_STRING},
};
#endif

HAL_IO_MAP_S gastHalIoMap[] =
	{
		{"input1", 0x201},
		{"input2", 0x200},
		{"input3", 0x202},
		{"input4", 0x207},
		{"input5", 0x204},
		{"input6", 0x203},
		{"input7", 0x205},
		{"input8", 0x206},
		{"input9", 0x318},
		{"input10", 0x31b},
		{"input11", 0x413},
		{"input12", 0x414},
		{"output1", 0xff01},
		{"output2", 0xff02},
		{"output3", 0xff03},
		{"output4", 0xff04},
		{"output5", 0xff05},
		{"output6", 0xff06},
		{"output7", 0xff07},
		{"output8", 0xff08},
};

static HAL_IO_HUMAN_CFG_S gastHalIoHumanCfg[] =
	{
		{{0}, &gsstHalIoCfgs.u32CapLight},
		{{0}, &gsstHalIoCfgs.u32CapLight_a},
		{{0}, &gsstHalIoCfgs.u32RailUpIO},
		{{0}, &gsstHalIoCfgs.u32RailDownIO},
		{{0}, &gsstHalIoCfgs.au32LoopIO[0]},
		{{0}, &gsstHalIoCfgs.au32LoopIO[1]},
		{{0}, &gsstHalIoCfgs.au32LoopIO[2]},
		{{0}, &gsstHalIoCfgs.au32LoopIO[3]},
		{{0}, &gsstHalIoCfgs.au32LoopIO[4]},
		{{0}, &gsstHalIoCfgs.au32LoopIO[5]},
};

static CONFIG_RECORD_S astRecords[] =
	{
		{"u32CapLight", gastHalIoHumanCfg[0].acName, CONFIG_TYPE_STRING},
		{"u32CapLight_a", gastHalIoHumanCfg[1].acName, CONFIG_TYPE_STRING},
		{"u32RailUpIO", gastHalIoHumanCfg[2].acName, CONFIG_TYPE_STRING},
		{"u32RailDownIO", gastHalIoHumanCfg[3].acName, CONFIG_TYPE_STRING},
		{"au32LoopIO0", gastHalIoHumanCfg[4].acName, CONFIG_TYPE_STRING},
		{"au32LoopIO1", gastHalIoHumanCfg[5].acName, CONFIG_TYPE_STRING},
		{"au32LoopIO2", gastHalIoHumanCfg[6].acName, CONFIG_TYPE_STRING},
		{"au32LoopIO3", gastHalIoHumanCfg[7].acName, CONFIG_TYPE_STRING},
		{"au32LoopIO4", gastHalIoHumanCfg[8].acName, CONFIG_TYPE_STRING},
		{"au32LoopIO5", gastHalIoHumanCfg[9].acName, CONFIG_TYPE_STRING},
};

static T_INT32 _HAL_IO_MapNameToNum(void)
{
	int i, j;

	for (i = 0; i < T_ARRAY_SIZE(astRecords); i++)
	{
		for (j = 0; j < T_ARRAY_SIZE(gastHalIoMap); j++)
		{
			if (!strcmp(gastHalIoHumanCfg[i].acName, gastHalIoMap[j].pcName))
			{
				*gastHalIoHumanCfg[i].pu32IoNum = gastHalIoMap[j].uIoNum;
			}
		}
	}
	return 0;
}

/*
*采用BANK+序号方法转换到SYS序号的方法，例如0x304 --> 68
排列序号的计算公式如下所示：
GPIO排列序号=（BANK - 1）X 32 + N
在公式中 BANK 为 GPIO 引脚所在的 BANK， N 则为引脚在该个 BANK 中的序号。以图 15.4
中 P3.4 为例， 其 BANK 值为 3， N 值为 4，因此排列序号为(3-1)*32+4=68。
*/
static T_UINT16 _HAL_IO_BankToSerialNO(int s32Num)
{
	T_UINT16 u16Io = 0;
	T_UINT8 u8Bank = 0;
	T_UINT8 u8Num = 0;

	u16Io = (T_UINT16)(s32Num & 0xffff);
	u8Bank = (T_UINT8)((u16Io >> 8) & 0xff);
	u8Num = (T_UINT8)(u16Io & 0xff);
	u16Io = (u8Bank - 1) * 32 + u8Num;
	//LOG_INFO("bank:0x%x, num:0x%x, io:%d", u8Bank, u8Num, u16Io);
	return u16Io;
}

static int _HAL_IO_CTRL(int s32Num, int s32Value)
{
	int fd_val = -1, fd_dir = -1, fd_export = -1;
	char path[32];
	char ctrl[16];
	T_UINT16 u16Io = 0;
	int nRet = 0;
	int val = 0;

	u16Io = _HAL_IO_BankToSerialNO(s32Num);

	// 导出设备
	memset(path, 0, sizeof(path));
	HAL_IO_GET_DIRECT_PATH(u16Io, path);
	fd_export = open(HAL_IO_EXPORT_PATH, O_WRONLY);
	if (fd_export < 0)
	{
		// LOG_ERROR("can not open export path:%s", HAL_IO_EXPORT_PATH);
		goto _error;
	}
	memset(ctrl, 0, sizeof(ctrl));
	sprintf(ctrl, "%d", u16Io);
	write(fd_export, ctrl, strlen(ctrl));

	// set direction
	memset(path, 0, sizeof(path));
	HAL_IO_GET_DIRECT_PATH(u16Io, path);
	fd_dir = open(path, O_RDWR);
	if (fd_dir < 0)
	{
		// TODO: 上线放开
		// LOG_ERROR("can not open path:%s", path);
		goto _error;
	}
	memset(ctrl, 0, sizeof(ctrl));
	if (s32Value < 0)
	{
		strcpy(ctrl, HAL_IO_DIR_IN);
	}
	else
	{
		strcpy(ctrl, HAL_IO_DIR_OUT);
	}
	nRet = write(fd_dir, ctrl, strlen(ctrl));
	if (nRet < 0)
	{
		LOG_ERROR("can not set io:%x direction %s", u16Io, ctrl);
		goto _error;
	}
	// open val path
	memset(path, 0, sizeof(path));
	HAL_IO_GET_VALUE_PATH(u16Io, path);
	fd_val = open(path, O_RDWR);
	if (fd_val < 0)
	{
		LOG_ERROR("can not open path:%s", path);
		goto _error;
	}
	if (s32Value < 0)
	{
		memset(ctrl, 0, sizeof(ctrl));
		nRet = read(fd_val, ctrl, sizeof(ctrl));
		if (nRet < 0)
		{
			LOG_ERROR("read val error");
			goto _error;
		}
		//LOG_INFO("read io:%s", ctrl);
		//val = strcmp(ctrl, HAL_IO_LOW_LEVEL) ? 1:0;
		val = atoi(ctrl);
	}
	else
	{
		// set value
		if (!s32Value)
		{
			nRet = write(fd_val, HAL_IO_LOW_LEVEL, strlen(HAL_IO_LOW_LEVEL));
		}
		else
		{
			nRet = write(fd_val, HAL_IO_HIGH_LEVEL, strlen(HAL_IO_HIGH_LEVEL));
		}
		if (nRet < 0)
		{
			LOG_ERROR("write io error:%d", s32Value);
			goto _error;
		}
		val = 0;
	}

	close(fd_export);
	close(fd_val);
	close(fd_dir);

	return val;

_error:
	if (fd_val > 0)
	{
		close(fd_val);
	}
	if (fd_dir > 0)
	{
		close(fd_dir);
	}
	if (fd_export > 0)
	{
		close(fd_export);
	}
	return -1;
}

/**
 s32Num:GPIO号，采用2字节即可表示:0xbbnn其中bb为GPIO的BANK，nn为GPIO所在BANK的序号.例如0x304表示第3BANK中的第4个。

*/
int _HAL_IO_Set(int s32Num, int s32Value)
{
	T_UINT16 u16IoChn = (T_UINT16)s32Num;

	if ((T_UINT16)s32Num == HAL_IO_INVALID_IO)
	{
		LOG_ERROR("invalid io");
		return -1;
	}

	if ((u16IoChn & 0xff00) == 0xff00)
	{
		T_UINT8 u8IoChn = (T_UINT8)(u16IoChn & 0x00ff);
		return HAL_MCU_SetIo(u8IoChn, (T_UINT8)s32Value);
	}
	return _HAL_IO_CTRL(s32Num, s32Value);
}

int _HAL_IO_Get(int s32Num)
{
	if ((T_UINT16)s32Num == HAL_IO_INVALID_IO)
	{
		LOG_ERROR("invalid io");
		return -1;
	}

	return _HAL_IO_CTRL(s32Num, -1);
}

static int _CMD_HAL_IO_Set(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	char abyHelp[] = "./cmd num value\n";
	int nParam = 0;
	int nParam2 = 0;

	if (pParam->nArgsNum != 2)
	{
		sprintf(pAck, "%s", abyHelp);
		return 0;
	}

	nParam = strtoul(pParam->abyArgs[0], NULL, 0);
	nParam2 = strtoul(pParam->abyArgs[1], NULL, 0);

	LOG_INFO("gpio:0x%x, value:%d", nParam, nParam2);

	_HAL_IO_Set(nParam, nParam2);

	sprintf(pAck, "ok\n");

	return 0;
}

static int _CMD_HAL_IO_Get(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	char abyHelp[] = "./cmd num\n";
	int nParam = 0;
	int nParam2 = 0;

	if (pParam->nArgsNum != 1)
	{
		sprintf(pAck, "%s", abyHelp);
		return 0;
	}

	nParam = strtoul(pParam->abyArgs[0], NULL, 0);

	nParam2 = _HAL_IO_Get(nParam);

	sprintf(pAck, "gpio:0x%x, value:%d\n", nParam, nParam2);

	return 0;
}

static int _CMD_HAL_IO_RailTest(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	char abyHelp[] = "./cmd param.0 for reset, 1 for up, 2 for down\n";
	int nParam = 0;
	int nParam2 = 0;

	if (pParam->nArgsNum != 1)
	{
		sprintf(pAck, "%s", abyHelp);
		return 0;
	}

	nParam = strtoul(pParam->abyArgs[0], NULL, 0);
	LOG_INFO("nParam:%d", nParam);
	if (nParam == 0)
	{
		HAL_IO_RailReset();
	}
	else if (nParam == 1)
	{
		HAL_IO_RailUp();
	}
	else if (nParam == 2)
	{
		HAL_IO_RailDown();
	}

	sprintf(pAck, "ok\n");

	return 0;
}

static int _CMD_HAL_IO_GetLoopStatus(CONSOLE_CMD_PARAM_S *pParam, char *pAck)
{
	T_UINT8 au8Loops[HAL_IO_MAX_LOOP];
	int i, n = 0;

	memset(au8Loops, 0, sizeof(au8Loops));
	HAL_IO_GetLoopStatus(au8Loops);
	for (i = 0; i < HAL_IO_MAX_LOOP; i++)
	{
		n += sprintf(pAck + n, "loop[%d]=%d\n", i, au8Loops[i]);
	}
	return 0;
}

int HAL_IO_Init(void)
{
	//return 0;

	pstHalIoCtx = (HAL_IO_CONTEXT_S *)malloc(sizeof(HAL_IO_CONTEXT_S));
	if (pstHalIoCtx == NULL)
	{
		LOG_ERROR("can not malloc memory for hal io context!");
		return -1;
	}
	memset((void *)pstHalIoCtx, 0, sizeof(HAL_IO_CONTEXT_S));

	memset(&gsstHalIoCfgs, 0, sizeof(gsstHalIoCfgs));

	Config_LoadFile(HAL_IO_CFG_FILE, astRecords, T_ARRAY_SIZE(astRecords));
	_HAL_IO_MapNameToNum();
	LOG_INFO("caplight io--0x%x", gsstHalIoCfgs.u32CapLight);
	LOG_INFO("caplight_a io--0x%x", gsstHalIoCfgs.u32CapLight_a);
	LOG_INFO("railup io----0x%x", gsstHalIoCfgs.u32RailUpIO);
	LOG_INFO("raildown io----0x%x", gsstHalIoCfgs.u32RailDownIO);
	LOG_INFO("loop0 io-----0x%x", gsstHalIoCfgs.au32LoopIO[0]);
	LOG_INFO("loop1 io-----0x%x", gsstHalIoCfgs.au32LoopIO[1]);
	LOG_INFO("loop2 io-----0x%x", gsstHalIoCfgs.au32LoopIO[2]);
	LOG_INFO("loop3 io-----0x%x", gsstHalIoCfgs.au32LoopIO[3]);
	LOG_INFO("loop4 io-----0x%x", gsstHalIoCfgs.au32LoopIO[4]);
	LOG_INFO("loop5 io-----0x%x", gsstHalIoCfgs.au32LoopIO[5]);

	Console_CmdAdd("t-hal-io-set", _CMD_HAL_IO_Set);
	Console_CmdAdd("t-hal-io-get", _CMD_HAL_IO_Get);
	Console_CmdAdd("t-hal-io-getloopstatus", _CMD_HAL_IO_GetLoopStatus);
	Console_CmdAdd("t-hal-io-railtest", _CMD_HAL_IO_RailTest);
	Console_CmdAdd("t-hal-io-light-on", HAL_IO_CapLightOn);
	Console_CmdAdd("t-hal-io-light-off", HAL_IO_CapLightOff);

	return 0;
}

int HAL_IO_DeInit(void)
{
	return 0;
}

int HAL_IO_RailUp(void)
{
	_HAL_IO_Set(gsstHalIoCfgs.u32RailUpIO, 1);
	usleep(500 * 1000);
	HAL_IO_RailReset();
	return 0;
}
int HAL_IO_RailDown(void)
{
	_HAL_IO_Set(gsstHalIoCfgs.u32RailDownIO, 1);
	usleep(500 * 1000);
	HAL_IO_RailReset();
	return 0;
}

int HAL_IO_RailReset(void)
{
	_HAL_IO_Set(gsstHalIoCfgs.u32RailUpIO, 0);
	_HAL_IO_Set(gsstHalIoCfgs.u32RailDownIO, 0);
	return 0;
}

int HAL_IO_CapLightOn(void)
{
	return _HAL_IO_Set(gsstHalIoCfgs.u32CapLight, 1);
}
int HAL_IO_CapLightOff(void)
{
	return _HAL_IO_Set(gsstHalIoCfgs.u32CapLight, 0);
}

/*pStatus: 应当传入数组 HAL_IO_MAX_LOOP*/
int HAL_IO_GetLoopStatus(T_UINT8 *pStatus)
{
	int s32Ret = 0;
	int i;
	int s32Status = 0;

	T_CHECK_PTR(pStatus);

	for (i = 0; i < HAL_IO_MAX_LOOP; i++)
	{
		s32Ret = _HAL_IO_Get(gsstHalIoCfgs.au32LoopIO[i]);
		if (s32Ret >= 0)
		{
			pStatus[i] = (T_UINT8)s32Ret;
		}
	}
	return 0;
}

static T_INT32 _HAL_IO_LoopThread(void *pvParam)
{
	while (pstHalIoCtx && pstHalIoCtx->nLoopRun)
	{
	}
}

int HAL_IO_LoopCallBackRegister(int s32Channel, T_CALLBACK fnCallBack)
{
	if (pstHalIoCtx == NULL)
	{
		LOG_ERROR("hal io not init");
		return -1;
	}
	if (s32Channel < 0 || s32Channel > HAL_IO_MAX_LOOP)
	{
		LOG_ERROR("invalid loop io channel:%d", s32Channel);
		return -1;
	}

	pstHalIoCtx->afnLoopCallBack[s32Channel] = fnCallBack;

	return 0;
}
